/*
 * Copyright 2010 - Jeremie Brebec
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.jb.sharedbills.client.stores;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.jb.sharedbills.client.gin.EventId;
import org.jb.sharedbills.shared.model.Event;
import org.jb.sharedbills.shared.model.notification.Notification;
import org.jb.sharedbills.shared.model.notification.NotificationListener;
import org.jb.sharedbills.shared.svc.EventServiceAsync;

import com.extjs.gxt.ui.client.widget.Info;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * 
 * @author jeremie
 *
 */
@Singleton
public class EventPeriodicSaver implements NotificationListener, EventSaver {

	/**
	 * 
	 */
	public EventPeriodicSaver() {
	}
	
	/**
	 * 
	 * @param event
	 */
	public void register( Event event, boolean isWriteable ) {
		
		if (!isWriteable) {
			setState(State.LOCKED);
			return;
		}
		event.getMembers().addListener( this );
		event.getGroups().addListener( this );
		event.getBills().addListener( this );
		event.addListener( this );
		
		this.event = event;
		
		scheduleSave();
		
		setState(State.COMMIT);
	}
	
	/**
	 * 
	 */
	@Override
	public void handle(Notification notification) {
		updated = true;
		
		if (getState() != State.UPDATING) {
			setState(State.DIRTY);
		}
	}
	
	/**
	 * 
	 */
	private void checkSave() {
		if (!updated) {
			scheduleSave();
			return;
		}
		
		save();
	}
	
	/**
	 * 
	 */
	public void save() {
		updated = false;
		setState(State.UPDATING);
		
		if (eventId == null)
			return;
		
		eventService.saveEvent( eventId, event, new AsyncCallback<Void>() {
			
			@Override
			public void onSuccess(Void result) {
				lastSave = new Date();
				
				if (failed) {
					Info.display("Sauvegarde", "Sauvegarde réussi");
				}
				failed = false;
				if (updated) {
					setState(State.PARTIAL_COMMIT);
				} else {
					setState(State.COMMIT);
				}
				scheduleSave();
			}
			
			@Override
			public void onFailure(Throwable caught) {
				if (!failed)
					Info.display("Sauvegarde", "Impossible de sauvegarder le document");
				failed = true;
				updated = true;
				setState(State.ERROR);
				scheduleSave();
			}
		});
		
	}

	/*
	 * (non-Javadoc)
	 * @see org.jb.sharedbills.client.stores.EventSaver#addListener(org.jb.sharedbills.client.stores.EventSaver.SaveStateListener)
	 */
	@Override
	public void addListener(SaveStateListener listener) {
		listeners.add(listener);
		
	}

	/*
	 * (non-Javadoc)
	 * @see org.jb.sharedbills.client.stores.EventSaver#getState()
	 */
	@Override
	public State getState() {
		return state;
	}
	
	/**
	 * 
	 * @param state
	 */
	private void setState( State state ) {
		if (this.state == state)
			return;
		
		this.state = state;
		
		for( SaveStateListener listener : listeners ) {
			listener.onStateChange(state);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.jb.sharedbills.client.stores.EventSaver#removeListener(org.jb.sharedbills.client.stores.EventSaver.SaveStateListener)
	 */
	@Override
	public void removeListener(SaveStateListener listener) {
		listeners.remove(listener);
	}
	
	/**
	 * 
	 */
	private void scheduleSave() {
		
		if (timer == null) {
			timer = new Timer() {
	
				@Override
				public void run() {
					checkSave();
				}
				
			};
		}
		
		timer.schedule( TIME );
	}
	
	private boolean updated;
	private boolean failed = false;
	private Timer timer;
	private final static int TIME = 10 * 1000;
	private State state = State.UPDATING;
	private List<SaveStateListener> listeners = new ArrayList<SaveStateListener>();

	@Inject
	private EventServiceAsync eventService;
	
	@Inject @EventId
	private String eventId;
	
	private Event event;
	
	@SuppressWarnings("unused")
	private Date lastSave;


	
}
